#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>

#define LED_MAJOR	200
#define LED_NAME	"led"

//register phy address
#define CCM_CCGR1_BASE			(0X020C406C)
#define SW_MUX_GPIO1_IO03_BASE  (0X020E0068)
#define SW_PAD_GPIO1_IO03_BASE  (0X020E02F4)
#define GPIO1_DR_BASE		    (0X0209C000)
#define GPIO1_GDIR_BASE		    (0X0209C004)

//虚拟地址指针
static void __iomem *IMX6U_CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

#define LEDOFF 0
#define LEDON  1

//led switch
static void led_switch(uint8_t sta)
{
	uint32_t val = 0;
	if(sta == LEDON)
	{
		val = readl(GPIO1_DR);
		val &= ~(1 << 3);		//bit3清零 打开led
		writel(val,GPIO1_DR);		//设置为输出
	}
	else if(sta == LEDOFF)
	{
		val = readl(GPIO1_DR);
		val |= (1 << 3);		//bit3置位 关闭开led
		writel(val,GPIO1_DR);		//设置为输出
	}
}


static int led_open(struct inode *inode, struct file *filp)
{
	//printk("led open!\r\n");
	return 0;
}

static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt)
{
	unsigned int retvalue = 0;
	unsigned char databuf[1];
	/* 接收用户空间传递给内核的数据并且打印出来 */
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0)
	{
		printk("kernel writefailed\r\n");
	}
	led_switch(databuf[0]);
	return 0;
}

static int led_close(struct inode *inode,struct file *filp)
{
	//printk("led release!\r\n");
	return 0;
}

static struct file_operations led_fops =
{
	.owner = THIS_MODULE,
	.write = led_write,
	.open = led_open,
	.release = led_close,
};



static int __init led_init(void)
{
	unsigned int retvalue = 0;
	unsigned int val_temp = 0;
	//step 1:adderss flex
	IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);  //地址映射
	SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE,4);
	SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE,4);
	GPIO1_DR = ioremap(GPIO1_DR_BASE,4);
	GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE,4);

	//GPIO clock
	val_temp = readl(IMX6U_CCM_CCGR1);
	val_temp &= ~(3<<26); //clear bit 26 27
	val_temp |= 3 << 26; //set bit 26 27
	writel(val_temp,IMX6U_CCM_CCGR1);

	writel(0x5,SW_MUX_GPIO1_IO03);		//复用为GPIO功能
	writel(0x10B0,SW_PAD_GPIO1_IO03);	//设置电气属性

	val_temp = readl(GPIO1_GDIR);
	val_temp |= 1 << 3;
	writel(val_temp,GPIO1_GDIR);		//设置为输出

	val_temp = readl(GPIO1_DR);
	val_temp &= ~(1 << 3);		//bit3清零 打开led
	writel(val_temp,GPIO1_DR);		//设置为输出


	retvalue = register_chrdev(LED_MAJOR, LED_NAME,&led_fops);

	if(retvalue < 0)
	{
		printk("led driver register failed\r\n");
		return -EIO;
	}
	else
	{
		printk("led driver init\r\n");
	}
	return retvalue;
}

static void __exit led_exit(void)
{
	unsigned int val_temp = 0;
	val_temp = readl(GPIO1_DR);
	val_temp |= (1 << 3);		//bit3置位 关闭开led
	writel(val_temp,GPIO1_DR);		//设置为输出

	iounmap(IMX6U_CCM_CCGR1);	//取消地址映射
	iounmap(SW_MUX_GPIO1_IO03);
	iounmap(SW_PAD_GPIO1_IO03);
	iounmap(GPIO1_DR);
	iounmap(GPIO1_GDIR);

	unregister_chrdev(LED_MAJOR,LED_NAME);
	printk("led driver exit\r\n");
}

/*register driver mount and unmount*/
module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eason");

